from typing import List, Optional, Type, Union

from pydantic import BaseModel
from tortoise import Model as TortoiseModel

from fastadmin.widgets import displays
from fastadmin.widgets.displays import Display
from fastadmin.widgets.filters import Filter
from fastadmin.widgets.inputs import Input


class Resource:
    """
    Base Resource
    """

    label: str
    icon: str = ""


class Link(Resource):
    url: str
    target: str = "_self"
    page_pre_title: Optional[str] = None
    page_title: Optional[str] = None


class Field(BaseModel):
    field_name: str
    label: str
    display: Display = Display()
    input: Input = Input()

    class Config:
        arbitrary_types_allowed = True


class Model(Resource):
    model: Type[TortoiseModel]
    fields: List[Union[str, Field]] = []
    page_size: int = 10
    page_pre_title: Optional[str] = None
    page_title: Optional[str] = None
    filters: Optional[List[Filter]] = []

    @classmethod
    async def get_filters(cls, values: Optional[dict] = None):
        if not values:
            values = {}
        ret = []
        for f in cls.filters:
            ret.append(await f.render(values.get(f.name)))
        return ret

    @classmethod
    def _get_fields_attr(cls, attr: str, display: bool = True):
        ret = []
        for field in cls.fields:
            if isinstance(field, str):
                ret.append(field)
            else:
                if display and isinstance(field.display, displays.InputOnly):
                    continue
                ret.append(getattr(field, attr))
        return ret or cls.model._meta.db_fields

    @classmethod
    def get_fields_name(cls, display: bool = True):
        return cls._get_fields_attr("field_name", display)

    @classmethod
    def get_fields(cls, display: bool = True):
        ret = []
        for field in cls.fields or cls.model._meta.db_fields:
            if isinstance(field, str):
                ret.append(Field(field_name=field, label=field))
            else:
                if display and isinstance(field.display, displays.InputOnly):
                    continue
                ret.append(field)
        return ret

    @classmethod
    def get_fields_label(cls, display: bool = True):
        return cls._get_fields_attr("label", display)


class Dropdown(Resource):
    resources: List[Type[Resource]]
